package com.yunzheng.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yunzheng.bo.MissionStatusCountBO;
import com.yunzheng.bo.NowIterationBO;
import com.yunzheng.bo.PlanProgressBO;
import com.yunzheng.bo.ProjectProgressBO;
import com.yunzheng.dto.ChangeProjectInfoDTO;
import com.yunzheng.dto.NewProjectDTO;
import com.yunzheng.entity.*;
import com.yunzheng.enums.ProjectStatusEnums;
import com.yunzheng.enums.RoleTypeEnums;
import com.yunzheng.exception.ServiceException;
import com.yunzheng.mapper.*;
import com.yunzheng.service.SysUserProjectRoleService;
import com.yunzheng.util.SnowFlakeUtil;
import com.yunzheng.vo.MyProjectListVO;
import com.yunzheng.vo.ProjectContentVO;
import com.yunzheng.vo.ProjectListVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunzheng.service.ProjectService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 郭思良
 * @create 2020-07-23 15:38
 */

@Service
@Slf4j
public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> implements ProjectService {

    @Resource
    private ProjectMapper projectMapper;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private SysUserProjectRoleMapper sysUserProjectRoleMapper;

    @Resource
    private SysUserProjectRoleService sysUserProjectRoleService;

    @Resource
    private ProjectPlanMapper projectPlanMapper;

    @Resource
    private ProjectMissionMapper projectMissionMapper;

    @Resource
    private ProjectFileMapper projectFileMapper;

    @Resource
    private ProjectNoticeMapper projectNoticeMapper;

    @Resource
    private ProjectFileContentMapper projectFileContentMapper;

    /**
     * 新增项目
     * @param input 项目信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void postNewProject(NewProjectDTO input) {
        // 获取projectId = 项目最大ID + 1
        // TODO: 2020/8/13 由于项目ID是varchar类型，所以展示的时候不太好排序，展示的问题需要纠正，目前使用的方法是查询数据库找到最大id，再+1
        String projectId = String.valueOf(projectMapper.selectProjectId() + 1);
        // 判断项目名是否出现重名情况，id不会出现重复的情况
        checkProjectName(input.getProjectName());
        // 将前端传来的数据存入 project 表中
        SysUser userManager = saveToProject(input, projectId);
        // 将团队中所有的用户信息传入 sys_user_project_role 表中
        saveUserToProject(projectId, userManager.getTeam(), userManager);
    }

    /**
     * 把用户添加到项目中
     * @param projectId 项目id
     * @param team 该项目所负责的团队
     * @param userManager 项目负责人
     */
    private void saveUserToProject(String projectId, String team, SysUser userManager) {
        //把所有该团队的所有成员都加入到项目-用户表中
        List<SysUser> sysUsers = sysUserMapper.selectList(new QueryWrapper<SysUser>().eq(SysUser.COL_TEAM, team));
        //所有的成员都是普通成员
        List<SysUserProjectRole> userList = new ArrayList<>();
        for (SysUser user : sysUsers){
            SysUserProjectRole sysUserProjectRole = new SysUserProjectRole(SnowFlakeUtil.nextId(), projectId, user.getUserId(),
                    String.valueOf(RoleTypeEnums.NORMAL_USER.getRoleId()));
            // 将项目负责人的角色改为 项目管理员
            if (user.getUserId().equals(userManager.getUserId())){
                sysUserProjectRole.setRoleId(String.valueOf(RoleTypeEnums.PROJECT_ADMIN.getRoleId()));
            }
            userList.add(sysUserProjectRole);
        }
        sysUserProjectRoleService.saveBatch(userList);
    }

    /**
     * 展示所有项目信息
     * @param offset 分页的页数
     * @return ProjectVO 项目列表
     */
    @Override
    public List<ProjectListVO> listAllProjects(Integer offset) {
        // 初始化分页并查询数据库获得项目
        IPage<ProjectListVO> page = new Page<>(offset, 10);
        page = projectMapper.selectProjectVO(page);
        pagePostProcess(page);
        // 获得项目的记录
        List<ProjectListVO> records = page.getRecords();
        List<String> idList = new ArrayList<>();
        for (ProjectListVO record : records) {
            String workTime = record.getWorkTime();
            workTime = workTime.substring(0, workTime.lastIndexOf("人"));
            record.setWorkTime(workTime);
            idList.add(record.getProjectId());
        }
        // 获得项目的其他信息
        if (records.size() > 0) {
            getProjectInfo(records, idList);
        }
        return records;
    }

    /**
     * 根据 项目id、项目名称、团队名、甲方、项目负责人 等信息进行搜索。
     * @param projects 搜索到的项目id的list
     * @return ProjectVO 项目列表
     */
    @Override
    public List<ProjectListVO> listProjectsByKey(List<Project> projects) {
        List<String> idList = projects.stream().map(Project::getProjectId).collect(Collectors.toList());
        // 查询项目的基本信息
        for (Project record : projects){
            String workTime = record.getWorkTime();
            workTime = workTime.substring(0, workTime.lastIndexOf("人"));
            record.setWorkTime(workTime);
        }
        // 包装成VO
        List<ProjectListVO> vos = getProjectVO(projects);
        getProjectInfo(vos, idList);
        return vos;
    }

    /**
     * 展示该项目的具体信息
     * @param projectId 项目id
     * @return 返回项目的具体信息
     */
    @Override
    public ProjectContentVO showProjectContent(String projectId) {
        Project project = projectMapper.selectById(projectId);
        if (project == null){
            throw new ServiceException("该项目不存在!");
        }
        String workTime = project.getWorkTime();
        workTime = workTime.substring(0, workTime.lastIndexOf("人"));
        project.setWorkTime(workTime);
        // 获取当前项目中不同状态工作项的数量
        List<MissionStatusCountBO> statusCount = projectMissionMapper.selectStatusCount(projectId);
        int undone = 0, ondoing = 0, done = 0;
        for (MissionStatusCountBO mission : statusCount) {
            if (mission.getMissionStatus() == 0){
                undone = mission.getMissionCount();
            }
            if (mission.getMissionStatus() == 1){
                ondoing = mission.getMissionCount();
            }
            if (mission.getMissionStatus() == 2){
                done = mission.getMissionCount();
            }
        }
        // 获取每个项目的每个阶段的进度，在平均一下
        double projectProgress;
        projectProgress = projectPlanMapper.selectProgress(projectId);
        // 获取每个项目的成员数量
        int memberCount = sysUserProjectRoleMapper.selectCount(new QueryWrapper<SysUserProjectRole>().eq(SysUserProjectRole.COL_PROJECT_ID, projectId));
        // 包装成VO
        ProjectContentVO projectContent = new ProjectContentVO(project.getProjectId(),project.getProjectName(),String.valueOf(project.getProjectStatus()),project.getProjectManager(),project.getWorkTime(), project.getPartyA(),projectProgress,
                project.getStartTime(),project.getFinishTime(),undone,ondoing,done,project.getDescription(),memberCount);
        String status = resetProjectStatus(projectContent.getProjectStatus());
        projectContent.setProjectStatus(status);
        return projectContent;
    }

    /**
     * 对项目列表的基本信息进行修改
     * @param input 修改项目的基本信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeProjectInfo(ChangeProjectInfoDTO input) {
        // 判断起止时间是否合法
        long time = Duration.between(input.getStartTime(), input.getFinishTime()).toDays();
        if(time < 0) {
            throw new ServiceException("结束时间早于开始时间");
        }
        Project project = new Project();
        BeanUtils.copyProperties(input, project);
        // 查找更新之前的项目负责人
        String user = projectMapper.selectById(input.getProjectId()).getProjectManager();
        //修改项目负责人 修改项目负责人会影响到项目团队名
        SysUser userManager = sysUserMapper.selectOne(new QueryWrapper<SysUser>().eq(SysUser.COL_REALNAME,input.getProjectManager()));
        project.setTeam(userManager.getTeam()).setWorkTime(input.getWorkTime() + "人天");
        projectMapper.updateById(project);
        //如果之前的项目负责人和现在的项目负责人不同 就修改团队
        if (!user.equals(input.getProjectManager())){
            //删除之前的所有成员
            sysUserProjectRoleMapper.delete(new QueryWrapper<SysUserProjectRole>().eq(SysUserProjectRole.COL_PROJECT_ID,input.getProjectId()));
            //加入新团队的成员
            saveUserToProject(input.getProjectId(), userManager.getTeam(), userManager);
        }
    }

    /**
     * 我所参与的项目
     * @param userId 当前用户的用户id
     */
    @Override
    public List<MyProjectListVO> myJoinProject(String userId) {
        // 根据当前用户的id 获得 所参与项目的id
        List<SysUserProjectRole> projectList = sysUserProjectRoleMapper.selectList(new QueryWrapper<SysUserProjectRole>().eq(SysUserProjectRole.COL_USER_ID, userId));
        if (projectList.size() == 0){
            return null;
        }
        return getMyProjectListVO(projectList);
}

    /**
     * 我所负责的项目
     * @param userId 当前用户的用户id
     */
    @Override
    public List<MyProjectListVO> myResponsibleProject(String userId) {
        // 根据当前用户的id 获得 所负责项目的id
        List<SysUserProjectRole> projectList = sysUserProjectRoleMapper.selectList(new QueryWrapper<SysUserProjectRole>()
                .eq(SysUserProjectRole.COL_USER_ID, userId)
                .eq(SysUserProjectRole.COL_ROLE_ID,RoleTypeEnums.PROJECT_ADMIN.getRoleId()));
        if (projectList.size() != 0){
            return getMyProjectListVO(projectList);
        }
        return null;
    }


    /**
     * 我负责的、我参与的项目进行数据处理
     * @param projectList 我负责的、我参与的项目
     * @return 包装成ProjectListVO的list集合
     */
    private List<MyProjectListVO> getMyProjectListVO(List<SysUserProjectRole> projectList){
        List<String> projectIds = projectList.stream().map(SysUserProjectRole::getProjectId).collect(Collectors.toList());
        // 返回信息 项目名、项目id、项目进度、起始时间、结束时间、甲方、负责人
        List<MyProjectListVO> myProjects = projectMapper.selectMyProject(projectIds);
        for (MyProjectListVO myProject : myProjects){
            if (myProject.getProjectProgress() == null){
                myProject.setProjectProgress(0.0);
            }
        }
        return myProjects;
    }

    /**
     * 删除项目接口
     * @param projectId 要删除的项目id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeProject(String projectId) {
        // 删除项目中的文档和文档的内容
        List<ProjectFile> files = projectFileMapper.selectList(new QueryWrapper<ProjectFile>().eq(ProjectFile.COL_FILE_PROJECT, projectId));
        // 如果存在files就删除，如果files没有的话就不用删除了
        if (files.size() > 0){
            List<String> fileIds = files.stream().map(ProjectFile::getFileId).collect(Collectors.toList());
            projectFileMapper.deleteBatchIds(fileIds);
            projectFileContentMapper.deleteBatchIds(fileIds);
            sysUserProjectRoleMapper.delete(new QueryWrapper<SysUserProjectRole>().eq(SysUserProjectRole.COL_PROJECT_ID,projectId));
        }
        projectPlanMapper.delete(new QueryWrapper<ProjectPlan>().eq(ProjectPlan.COL_PROJECT_ID,projectId));
        projectMissionMapper.delete(new QueryWrapper<ProjectMission>().eq(ProjectMission.COL_MISSION_PROJECT,projectId));
        projectNoticeMapper.delete(new QueryWrapper<ProjectNotice>().eq(ProjectNotice.COL_NOTICE_PROJECT,projectId));
        projectMapper.deleteById(projectId);
    }

    /**
     * 批量删除项目接口
     * @param projectIds 要删除的项目id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeProjects(List<String> projectIds) {
        projectMapper.deleteBatchIds(projectIds);
        projectPlanMapper.delete(new QueryWrapper<ProjectPlan>().in(ProjectPlan.COL_PROJECT_ID,projectIds));
        projectMissionMapper.delete(new QueryWrapper<ProjectMission>().in(ProjectMission.COL_MISSION_PROJECT,projectIds));
        projectNoticeMapper.delete(new QueryWrapper<ProjectNotice>().in(ProjectNotice.COL_NOTICE_PROJECT,projectIds));
        // 删除项目中的文档和文档的内容
        List<ProjectFile> files = projectFileMapper.selectList(new QueryWrapper<ProjectFile>().in(ProjectFile.COL_FILE_PROJECT, projectIds));
        // 如果存在files就删除，如果files没有的话就不用删除了
        if (files.size() > 0){
            List<String> fileIds = files.stream().map(ProjectFile::getFileId).collect(Collectors.toList());
            projectFileMapper.deleteBatchIds(fileIds);
            projectFileContentMapper.deleteBatchIds(fileIds);
            sysUserProjectRoleMapper.delete(new QueryWrapper<SysUserProjectRole>().in(SysUserProjectRole.COL_PROJECT_ID,projectIds));
        }

    }

    @Override
    public List<ProjectListVO> listMyProjects(String userId) {
        List<String> projectIds = sysUserProjectRoleMapper.selectList(new QueryWrapper<SysUserProjectRole>().
                eq(SysUserProjectRole.COL_USER_ID, userId)).stream().map(SysUserProjectRole::getProjectId).
                collect(Collectors.toList());
        List<Project> projects = projectMapper.selectBatchIds(projectIds);
        for (Project record : projects){
            String workTime = record.getWorkTime();
            workTime = workTime.substring(0, workTime.lastIndexOf("人"));
            record.setWorkTime(workTime);
        }
        // 包装成VO
        List<ProjectListVO> vos = getProjectVO(projects);
        getProjectInfo(vos, projectIds);
        return vos;
    }


    /**
     * 通过映射将Project转换成ProjectListVO
     * @param records 项目查询记录
     * @return 返回ProjectVO的list集合
     */
    private List<ProjectListVO> getProjectVO(List<Project> records) {
        List<ProjectListVO> list = new ArrayList<>();
        // 通过映射进行转换
        for (Project project : records){
            ProjectListVO projectListVO = new ProjectListVO();
            BeanUtils.copyProperties(project, projectListVO);
            projectListVO.setProjectStatus(String.valueOf(project.getProjectStatus()))
                    .setLevel(Integer.valueOf(project.getLevel()));
            list.add(projectListVO);
        }
        return list;
    }

    /**
     * 查询所有信息ID查询计算其他重要信息
     * @param records 项目查询记录
     * @param idList records中的项目ID
     */
    private void getProjectInfo(List<ProjectListVO> records, List<String> idList) {
        // 获得每个项目的当前迭代
        List<NowIterationBO> nowIteration = projectMapper.selectNowIteration(idList);
        // 获得每个项目的项目进度
        List<ProjectProgressBO> projectProgress = projectPlanMapper.selectProjectProgress(idList);
        // 获得每个项目的当前阶段、当前阶段进度
        List<PlanProgressBO> planProgress = projectPlanMapper.selectPlanProgress(idList);

        //将这几个list合并在一起
        for (ProjectListVO x : records) {
            for (NowIterationBO y : nowIteration) {
                if (y.getIterationProject().equals(x.getProjectId())) {
                    x.setNowIteration(y.getIterationName());
                }
            }
            for (ProjectProgressBO z : projectProgress){
                if (z.getProjectId().equals(x.getProjectId())){
                    x.setProjectProgress(z.getProjectProgress());
                }
            }
            for (PlanProgressBO u : planProgress){
                if (u.getProjectId().equals(x.getProjectId())){
                    x.setPlanName(u.getPlanName());
                    x.setPlanProgress(u.getProgress());
                }
            }
        }
        // 初始化一些null值
        for (ProjectListVO vo : records){
            String status = resetProjectStatus(vo.getProjectStatus());
            vo.setProjectStatus(status);
            if (vo.getProjectProgress() == null){
                vo.setProjectProgress(0.0);
            }
            if (vo.getPlanProgress() == null){
                vo.setPlanProgress(0.0);
            }
        }

    }

    /**
     * 对分页进行后续的记录及操作
     * @param page 分页查询信息
     */
    private void pagePostProcess(IPage<ProjectListVO> page) {
        if (page.getTotal() >= 0) {
            log.debug("项目管理基本信息:查询成功");
            long pageCurrent = page.getCurrent();
            if (page.getRecords().size() != 0) {
                log.debug("项目管理基本信息：返回给前端第"+ pageCurrent +"页上的所有信息");
            } else {
                throw new ServiceException("第" + pageCurrent + "页上没有信息！！！");
            }
        } else {
            throw new ServiceException("查询失败，传入参数错误！！！");
        }
    }


    /**
     * 将前端传来的数据存入 project 表中
     * @param input 新项目信息
     */
    private SysUser saveToProject(NewProjectDTO input, String projectId) {
        Project project = new Project();
        //DTO中的是名字，所以需要通过名字来获取信息
        SysUser userManager = sysUserMapper.selectOne(new QueryWrapper<SysUser>().eq(SysUser.COL_REALNAME, input.getProjectManager()));
        // 如果没有传work_time 就 自动为 finish - start
        long days = Duration.between(input.getStartTime(), input.getFinishTime()).toDays();
        if (input.getWorkTime() != 0){
            days = input.getWorkTime();
        }
        // 构造project对象，并插入到数据库中 project_manager是名字、partyA是名字、
        project.setProjectId(projectId).setProjectName(input.getProjectName()).setProjectManager(input.getProjectManager()).
                setPartyA(input.getPartyA()).setLevel(input.getLevel()).setWorkTime(days + "人天").setStartTime(input.getStartTime())
                .setFinishTime(input.getFinishTime()).setTeam(userManager.getTeam()).setDescription(input.getDescription());
        projectMapper.insert(project);
        return userManager;
    }

    /**
     * 判断项目名是否出现重名情况
     * @param projectName 项目名
     */
    private void checkProjectName(String projectName) {
        int rows = projectMapper.selectCount(new QueryWrapper<Project>().eq(Project.COL_PROJECT_NAME, projectName));
        if (rows > 0){
            throw new ServiceException("不能有相同名称的项目，请重新命名！");
        }
    }

    /**
     * 根据当前项目状态码获得状态信息
     * @param status 项目状态码
     * @return 状态信息
     */
    private String resetProjectStatus(String status){
        if (status.equals(String.valueOf(ProjectStatusEnums.UNDONE.getStatus()))) {
            status = ProjectStatusEnums.UNDONE.getValue();
        }else if (status.equals(String.valueOf(ProjectStatusEnums.ONGOING.getStatus()))){
            status = ProjectStatusEnums.ONGOING.getValue();
        }else{
            status = ProjectStatusEnums.DONE.getValue();
        }
        return status;
    }
}

